//////////////////////////////////////////////////////////////////////
// EnvMapResampler.h
//
// SHEN Fangyang
// me@shenfy.com
//
// Copyright (C) SHEN Fangyang, 2011, All rights reserved.
//////////////////////////////////////////////////////////////////////

//////////////////////////////////////////////////////////////////////
//  Brief Description
//
//	Conversion between environment map parameterizations,
//	bind to samplers to use.
//
//////////////////////////////////////////////////////////////////////
#pragma once

#include <CubemapSampler.h>
#include <OctmapSampler.h>
#include <HemiOctmapSampler.h>
#include <Logger.h>
#include <LA/Vec3.h>

#pragma comment (lib, W32LIB_BIN_FILE)

namespace grflib
{
    namespace image
    {
        //Class declaration
        namespace envmap_resampler
        {
            //Output buffers should be pre-allocated
            template<typename T>
            bool CubemapToOctmap(COctmapSampler<T> *pOut, CCubemapSampler<T> *pIn, const LA::CVec3<float> &N);
            template<typename T>
            bool OctmapToHemiOctmap(CHemiOctmapSampler<T> *pOut, COctmapSampler<T> *pIn, const LA::CVec3<float> &N);
            template<typename T>
            bool CubemapToHemiOctmap(CHemiOctmapSampler<T> *pOut, CCubemapSampler<T> *pIn, const LA::CVec3<float> &N);
            template<typename T>
            bool VCubemapToCubemap(CCubemapSampler<T> *pOut, CVCrossCubemapSampler<T> *pIn, const LA::CVec3<float> &N);


            template<typename T>
            bool CubemapToOctmap(COctmapSampler<T> *pOut, CCubemapSampler<T> *pIn, const LA::CVec3<float> &N)
            {
                UINT widthIn, heightIn, nCh, widthOut, heightOut;
                if (!pOut || !pIn)
                    return false;

                widthIn = pIn->GetWidth();
                heightIn = pIn->GetHeight();
                widthOut = pOut->GetWidth();
                heightOut = pOut->GetHeight();
                nCh = pOut->GetBPP() / 8 / sizeof(T);

                float u = 0.0f, v = 0.0f;
                float vecx = 0.0f, vecy = 0.0f, vecz = 0.0f;
                T pixel[8]; //up to 8 channels

                LA::CVec3<float> lx, ly(N), lz; //normal vector as Y-axis
                LA::CVec3<float> gx, gy, gz;
                LA::CVec3<float> global, local; //sample direction vectors

                ly.Normalize();
                LA::CVec3<float>::TangentVectors(lx, ly, lz);
                LA::CVec3<float>::CoordTransform(gx, LA::CVec3<float>(1.0f, 0.0f, 0.0f), lx, ly, lz);
                LA::CVec3<float>::CoordTransform(gy, LA::CVec3<float>(0.0f, 1.0f, 0.0f), lx, ly, lz);
                LA::CVec3<float>::CoordTransform(gz, LA::CVec3<float>(0.0f, 0.0f, 1.0f), lx, ly, lz);

                for (UINT y = 0; y < heightOut; y++)
                {
                    for (UINT x = 0; x < widthOut; x++)
                    {
                        u = (x + 0.5f) / widthOut;
                        v = (y + 0.5f) / heightOut;
                        COctmapSampler<T>::Tex2DQuadTo3D(u, v, vecx, vecy, vecz);

						local.SetValue(vecx, vecy, vecz);
                        LA::CVec3<float>::CoordTransform(global, local, gx, gy, gz);

                        pIn->texCUBE(global._x, global._y, global._z, pixel);
                        for (UINT c = 0; c < nCh; c++)
                            (*pOut)(x, y, c) = pixel[c];
                    }
                }
                return true;
            }

            template<typename T>
            bool OctmapToHemiOctmap(CHemiOctmapSampler<T> *pOut, COctmapSampler<T> *pIn, const LA::CVec3<float> &N)
            {
                UINT widthIn, heightIn, nCh, widthOut, heightOut;
                if (!pOut || !pIn)
                    return false;

                widthIn = pIn->GetWidth();
                heightIn = pIn->GetHeight();
                widthOut = pOut->GetWidth();
                heightOut = pOut->GetHeight();
                nCh = pOut->GetBPP() / 8 / sizeof(T);

                float uo = 0.0f, vo = 0.0f;
                float ui = 0.0f, vi = 0.0f;

                LA::CVec3<float> lx, ly(N), lz; //normal vector as Y-axis
                LA::CVec3<float> gx, gy, gz;
                LA::CVec3<float> global, local; //sample direction vectors

                ly.Normalize();
                LA::CVec3<float>::TangentVectors(lx, ly, lz);
                LA::CVec3<float>::CoordTransform(gx, LA::CVec3<float>(1.0f, 0.0f, 0.0f), lx, ly, lz);
                LA::CVec3<float>::CoordTransform(gy, LA::CVec3<float>(0.0f, 1.0f, 0.0f), lx, ly, lz);
                LA::CVec3<float>::CoordTransform(gz, LA::CVec3<float>(0.0f, 0.0f, 1.0f), lx, ly, lz);

                float vecx, vecy, vecz;

                for (UINT y = 0; y < heightOut; y++)
                {
                    for (UINT x = 0; x < widthOut; x++)
                    {
                        uo = (x + 0.5f) / widthOut;
                        vo = (y + 0.5f) / heightOut;

                        //project to local 3D coord
                        CHemiOctmapSampler<T>::Tex2DTo3D(uo, vo, vecx, vecy, vecz);

                        //rotate to global coord
                        local.SetValue(vecx, vecy, vecz);
                        LA::CVec3<float>::CoordTransform(global, local, gx, gy, gz);

                        for (UINT c = 0; c < nCh; c++)
                            (*pOut)(x, y, c) = pIn->texOctQuad(global._x, global._y, global._z, c);
                    }
                }
                return true;
            }

            template<typename T>
            bool CubemapToHemiOctmap(CHemiOctmapSampler<T> *pOut, CCubemapSampler<T> *pIn, const LA::CVec3<float> &N)
            {
                UINT widthIn, heightIn, nCh, widthOut, heightOut;
                if (!pOut || !pIn)
                    return false;

                widthIn = pIn->GetWidth();
                heightIn = pIn->GetHeight();
                widthOut = pOut->GetWidth();
                heightOut = pOut->GetHeight();
                nCh = pOut->GetBPP() / 8 / sizeof(T);

                float uo = 0.0f, vo = 0.0f;
                float ui = 0.0f, vi = 0.0f;

                LA::CVec3<float> lx, ly(N), lz; //normal vector as Y-axis
                LA::CVec3<float> gx, gy, gz;
                LA::CVec3<float> global, local; //sample direction vectors

                ly.Normalize();
                LA::CVec3<float>::TangentVectors(lx, ly, lz);
                LA::CVec3<float>::CoordTransform(gx, LA::CVec3<float>(1.0f, 0.0f, 0.0f), lx, ly, lz);
                LA::CVec3<float>::CoordTransform(gy, LA::CVec3<float>(0.0f, 1.0f, 0.0f), lx, ly, lz);
                LA::CVec3<float>::CoordTransform(gz, LA::CVec3<float>(0.0f, 0.0f, 1.0f), lx, ly, lz);

                float vecx, vecy, vecz;
                T pixel[8]; //support up to 8 channels

                for (UINT y = 0; y < heightOut; y++)
                {
                    for (UINT x = 0; x < widthOut; x++)
                    {
                        uo = (x + 0.5f) / widthOut;
                        vo = (y + 0.5f) / heightOut;

                        //project to local 3D coord
                        CHemiOctmapSampler<T>::Tex2DTo3D(uo, vo, vecx, vecy, vecz);

                        //rotate to global coord
                        local.SetValue(vecx, vecy, vecz);
                        LA::CVec3<float>::CoordTransform(global, local, gx, gy, gz);

                        pIn->texCUBE(global._x, global._y, global._z, pixel);
                        for (UINT c = 0; c < nCh; c++)
                            (*pOut)(x, y, c) = pixel[c];
                    }
                }
                return true;
            }

            template<typename T>
            bool VCubemapToCubemap(CCubemapSampler<T> *pOut, CVCrossCubemapSampler<T> *pIn, const LA::CVec3<float> &N)
            {
                UINT widthIn, heightIn, nCh, widthOut, heightOut, faceWidthIn, faceWidthOut;
                if (!pOut || !pIn)
                    return false;

                widthIn = pIn->GetWidth();
                heightIn = pIn->GetHeight();
                faceWidthIn = widthIn / 3;
                widthOut = pOut->GetWidth();
                heightOut = pOut->GetHeight();
                faceWidthOut = widthOut / 6;
                nCh = pOut->GetBPP() / 8 / sizeof(T);

                float uo = 0.0f, vo = 0.0f;
                float ui = 0.0f, vi = 0.0f;

                LA::CVec3<float> lx, ly(N), lz; //normal vector as Y-axis
                LA::CVec3<float> gx, gy, gz;
                LA::CVec3<float> global, local; //sample direction vectors

                ly.Normalize();
                LA::CVec3<float>::TangentVectors(lx, ly, lz);
                LA::CVec3<float>::CoordTransform(gx, LA::CVec3<float>(1.0f, 0.0f, 0.0f), lx, ly, lz);
                LA::CVec3<float>::CoordTransform(gy, LA::CVec3<float>(0.0f, 1.0f, 0.0f), lx, ly, lz);
                LA::CVec3<float>::CoordTransform(gz, LA::CVec3<float>(0.0f, 0.0f, 1.0f), lx, ly, lz);

                float vecx, vecy, vecz;
                T pixel[8]; //support up to 8 channels

                for (UINT face = 0; face < 6; face++)
                {
                    for (UINT y = 0; y < faceWidthOut; y++)
                    {
                        for (UINT x = 0; x < faceWidthOut; x++)
                        {
                            uo = (x + 0.5f) / faceWidthOut;
                            vo = (y + 0.5f) / faceWidthOut;

                            //project to local 3D coord
                            CCubemapSampler<T>::Tex2DTo3D(uo, vo, face, vecx, vecy, vecz);

                            //rotate to global coord
                            local.SetValue(vecx, vecy, vecz);
                            LA::CVec3<float>::CoordTransform(global, local, gx, gy, gz);

                            pIn->texCUBE(global._x, global._y, global._z, pixel);
                            for (UINT c = 0; c < nCh; c++)
                                (*pOut)(face, x, y, c) = pixel[c];
                        }
                    }
                }
                return true;
            }

            template<typename T>
            bool CubemapToVCubemap(CVCrossCubemapSampler<T> *pOut, CCubemapSampler<T> *pIn, const LA::CVec3<float> &N)
            {
                UINT widthIn, heightIn, nCh, widthOut, heightOut, faceWidthIn, faceWidthOut;
                if (!pOut || !pIn)
                    return false;

                widthIn = pIn->GetWidth();
                heightIn = pIn->GetHeight();
                faceWidthIn = widthIn / 6;
                widthOut = pOut->GetWidth();
                heightOut = pOut->GetHeight();
                faceWidthOut = widthOut / 3;
                nCh = pOut->GetBPP() / 8 / sizeof(T);

                float uo = 0.0f, vo = 0.0f;
                float ui = 0.0f, vi = 0.0f;

                LA::CVec3<float> lx, ly(N), lz; //normal vector as Y-axis
                LA::CVec3<float> gx, gy, gz;
                LA::CVec3<float> global, local; //sample direction vectors

                ly.Normalize();
                LA::CVec3<float>::TangentVectors(lx, ly, lz);
                LA::CVec3<float>::CoordTransform(gx, LA::CVec3<float>(1.0f, 0.0f, 0.0f), lx, ly, lz);
                LA::CVec3<float>::CoordTransform(gy, LA::CVec3<float>(0.0f, 1.0f, 0.0f), lx, ly, lz);
                LA::CVec3<float>::CoordTransform(gz, LA::CVec3<float>(0.0f, 0.0f, 1.0f), lx, ly, lz);

                float vecx, vecy, vecz;
                T pixel[8]; //support up to 8 channels

                for (UINT face = 0; face < 6; face++)
                {
                    for (UINT y = 0; y < faceWidthOut; y++)
                    {
                        for (UINT x = 0; x < faceWidthOut; x++)
                        {
                            uo = (x + 0.5f) / faceWidthOut;
                            vo = (y + 0.5f) / faceWidthOut;

                            //project to local 3D coord
                            CVCrossCubemapSampler<T>::Tex2DTo3D(uo, vo, face, vecx, vecy, vecz);

                            //rotate to global coord
                            local.SetValue(vecx, vecy, vecz);
                            LA::CVec3<float>::CoordTransform(global, local, gx, gy, gz);

                            pIn->texCUBE(global._x, global._y, global._z, pixel);
                            for (UINT c = 0; c < nCh; c++)
                                (*pOut)(face, x, y, c) = pixel[c];
                        }
                    }
                }
                return true;
            }

        } //namespace envmap_resampler
    } //namespace image
} //namespace grflib